import { Vector3 } from 'three'
import { Group, Image } from 'zrender'
import { CqkjMap } from '../Basic/CqkjMap'
import { MapElementOption, MapElement } from '../Basic/MapElement'
import { setValue, loadImage } from '../Util/common'
import { MOCATORMAX, XTO1, YTO1 } from '../Util/static'


const offsets: Vector3[] = [new Vector3(-1, -1, 0), new Vector3(0, -1, 0), new Vector3(0, 0, 0), new Vector3(-1, 0, 0)]

for (let j = 1; j < 8; j++) {
  for (let i = 1 - j; i < j + 2; i++) {
    offsets.push(new Vector3(-1 + i, -1 - j, 0))
    offsets.push(new Vector3(j, -1 + i, 0))
    offsets.push(new Vector3(-i, j, 0))
    offsets.push(new Vector3(-1 - j, -i, 0))
  }
}
offsets.reverse()
export interface TileMapOption extends MapElementOption {
  url: string
  subdomains: string
  workers: number
  z: number
}

export class TileMap extends MapElement<TileMapOption> {
  tiles = new Map<string, Image>()
  root = new Group()
  key = new Vector3()
  tasks: Vector3[] = []
  busy = 0
  get show (): boolean {
    return this.option.show
  }

  set show (val: boolean) {
    setValue(this.option, 'show', val, (v) => {
      if (v) {
        this.onAfterViewChange()
        this.root.show()
      } else {
        this.root.hide()
      }
    })
  }

  constructor (map: CqkjMap, option: Partial<TileMapOption> = {}) {
    super(map, Object.assign({
      name: '瓦片底图',
      show: true,
      url: 'https://api.mapbox.com/styles/v1/zy-01/clbbopz4y000q15mslpuydwy2/tiles/256/{z}/{x}/{y}?access_token=pk.eyJ1IjoienktMDEiLCJhIjoiY2xiYm9qZmxzMGthZzN4bG1lMGd4dGs1YyJ9.gbzGyrJNb3U0zQMR_a2EGQ',
      subdomains: '01234567',
      workers: 6,
      z: 0
    }, option))
    map.ground.root.add(this.root)
    map.show && this.show && this.onAfterViewChange()
  }

  onAfterViewChange (): void {
    const { map, option } = this
    const { key } = map.ground
    if (!this.key.equals(key)) {
      this.tasks = offsets.map(e => key.clone().add(e))
      this.key.copy(key)
      this.root.removeAll()
      for (let i = 0; i < option.workers; i++) {
        this.doTask()
      }
    }
  }

  async doTask () {
    if (this.busy >= this.option.workers) {
      return
    }
    const task = this.tasks.pop()
    if (task) {
      const key = `${task.x}_${task.y}_${task.z}`
      let tile = this.tiles.get(key)
      if (!tile) {
        const { url, subdomains } = this.option
        const scale = Math.pow(2, task.z)
        const size = MOCATORMAX * 2 / scale
        tile = new Image({
          x: XTO1.backward(task.x / scale),
          y: YTO1.backward(task.y / scale),
          scaleY: -1,
          style: {
            width: size,
            height: size,
          }
        })
        this.tiles.set(key, tile)
        this.busy++
        const x = task.x % scale
        const image = await loadImage(url
          .replace('{s}', subdomains[(task.x + task.y) % subdomains.length])
          .replace('{x}', x.toFixed(0))
          .replace('{y}', task.y.toFixed(0))
          .replace('{z}', task.z.toFixed(0)))
        this.busy--
        tile.attr({
          style: {
            image
          }
        })
      }
      this.root.add(tile)
      this.doTask()
    }
  }

  onDestroy (): void {
    const { map } = this
    map.ground.root.remove(this.root)
  }
}
